package lua

import (
	"HyperNexus/hyperNexus/goModule/hyper"
	"HyperNexus/hyperNexus/hyperNexus/interfaces"
	"HyperNexus/hyperNexus/utils"
	"bufio"
	"context"
	"fmt"
	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/container/gmap"
	lua "github.com/yuin/gopher-lua"
	"github.com/yuin/gopher-lua/parse"
	"os"
	"path/filepath"
)

var libCompiledList garray.Array

var serviceCompliedMap gmap.StrAnyMap

type LuaHelper struct {
	L *lua.LState
}

func NewLuaHelper() *LuaHelper {
	return &LuaHelper{
		L: lua.NewState(),
	}
}

func (l *LuaHelper) Init() {
	l.initGoModule()
	l.initLuaPath()
	l.initLibLua()
	l.InitServiceLua("TestService")
}

func (l *LuaHelper) initLuaPath() {
	luaSrcPaths := garray.NewStrArray()
	luaSrcPaths.Append("./service")
	luaSrcPaths.Append("./lualib")
	l.AppendLuaPath(luaSrcPaths)
}

func (l *LuaHelper) initGoModule() {
	l.L.PreloadModule("hyper", func(L *lua.LState) int {
		mod := L.SetFuncs(L.NewTable(), hyper.HyperExports)
		L.SetField(mod, "name", lua.LString("value"))
		L.Push(mod)
		return 1
	})
}

func (l *LuaHelper) initLibLua() {
	libCompiledList.Iterator(func(k int, v interface{}) bool {
		err := DoCompiledFile(l.L, v.(*lua.FunctionProto))
		if err != nil {
			fmt.Printf("load lua err %s", err.Error())
		}
		return true
	})
}

func (l *LuaHelper) InitServiceLua(serviceName string) {
	p := serviceCompliedMap.Get(serviceName)
	if p == nil {
		fmt.Printf("service %s not found", serviceName)
		return
	}
	err := DoCompiledFile(l.L, p.(*lua.FunctionProto))
	if err != nil {
		fmt.Printf("load lua err %s", err.Error())
	}
}

func (l *LuaHelper) AppendLuaPath(paths *garray.StrArray) {
	if paths.Len() > 0 {
		pathStr := ""
		paths.Iterator(func(k int, v string) bool {
			// 转换为绝对路径并检查存在性
			absDir, err := filepath.Abs(v)
			if err != nil {
				interfaces.IHyperNexusApp.Logger().Errorf(context.Background(), "警告：目录转换失败 %s: %v", v, err)
				return true
			}
			if _, err := os.Stat(absDir); os.IsNotExist(err) {
				interfaces.IHyperNexusApp.Logger().Errorf(context.Background(), "警告：目录不存在 %s", absDir)
				return true
			}
			// 转换为Lua路径格式：使用斜杠和?.lua通配符
			luaPath := filepath.ToSlash(filepath.Join(absDir, "?.lua"))
			pathStr += ";" + luaPath
			return true
		})
		cmd := fmt.Sprintf(`package.path = package.path .. '%s'`, pathStr)
		if err := l.L.DoString(cmd); err != nil {
			interfaces.IHyperNexusApp.Logger().Fatalf(context.Background(), "设置package.path失败: %v", err)
		}
	}
}

func (l *LuaHelper) Close() {
	if l.L != nil {
		l.L.Close()
	}
}

// CompileLua reads the passed lua file from disk and compiles it.
func CompileLua(filePath string) (*lua.FunctionProto, error) {
	file, err := os.Open(filePath)
	defer file.Close()
	if err != nil {
		return nil, err
	}
	reader := bufio.NewReader(file)
	chunk, err := parse.Parse(reader, filePath)
	if err != nil {
		return nil, err
	}
	proto, err := lua.Compile(chunk, filePath)
	if err != nil {
		return nil, err
	}
	return proto, nil
}

// DoCompiledFile takes a FunctionProto, as returned by CompileLua, and runs it in the LState. It is equivalent
// to calling DoFile on the LState with the original source file.
func DoCompiledFile(L *lua.LState, proto *lua.FunctionProto) error {
	lfunc := L.NewFunctionFromProto(proto)
	L.Push(lfunc)
	return L.PCall(0, lua.MultRet, nil)
}

func CompliedLibLua(libPath *garray.StrArray) {
	libPath.Iterator(func(k int, v string) bool {
		// 扫描路径下所有lua文件
		files, e := utils.FindLuaFiles(v)
		if e != nil {
			fmt.Printf("load lua err %s", e.Error())
			return true
		}
		for _, filePath := range files {
			p, err := CompileLua(filePath)
			if err != nil {
				fmt.Printf("load lua err %s", err.Error())
				return true
			}
			libCompiledList.Append(p)
		}
		return true
	})
}

func CompliedServiceLua(serviceName string, serviceFilePath string) {
	p, err := CompileLua(serviceFilePath)
	if err != nil {
		fmt.Printf("CompliedServiceLua err %s", err.Error())
	}
	serviceCompliedMap.Set(serviceName, p)
}
